package kompics.ring.components;


import java.util.HashMap;
import java.util.Map;

import kompics.ring.events.PeerContInit;
import kompics.ring.events.PingMessage;
import kompics.ring.events.PongMessage;
import kompics.ring.events.ReadValueEvent;
import kompics.ring.events.StoreKeyEvent;

import se.sics.kompics.ComponentDefinition;
import se.sics.kompics.Handler;
import se.sics.kompics.Positive;
import se.sics.kompics.address.Address;
import se.sics.kompics.network.Network;

public class SimplePeer extends ComponentDefinition {

	private Address successor;
	private Address self;
	Positive<Network> network = positive(Network.class);
	
	/*
	 * Node 1 = 1-100
	 * Node 2 = 101-200
	 * Node 3 = 201-300
	 * Node 4 = 301-400
	 * 
	 */
	private final int MAX_SYSTEM_RANGE = 400;
	private final int MIN_SYSTEM_RANGE = 1;

	
	Map<Integer, Integer> keyStore;

	public SimplePeer() {
		subscribe(handleInit, control);
		subscribe(handlePingMsg, network);
		subscribe(handlePongMsg, network);
		
		keyStore = new HashMap<Integer, Integer>();
		
		subscribe(handleStoreKey, network);
		subscribe(handleReadValue, network);
		
	}
		
    Handler<PingMessage> handlePingMsg = new Handler<PingMessage>() {
       public void handle(PingMessage event) {
    	   System.out.println("[Address]: "+self+" received a ping message....");
           trigger(new PongMessage(self, successor), network );
           System.out.println("[Address]: "+self+" sent a pong  message to "+ event.getSource());
        }
    };
    
    Handler<PongMessage> handlePongMsg = new Handler<PongMessage>() {
        public void handle(PongMessage event) {
            System.out.println("[Address]: "+self+" received a pong message from: "+ event.getSource());
        }
    };
    
    Handler<ReadValueEvent> handleReadValue = new Handler<ReadValueEvent>() {
        public void handle(ReadValueEvent event) {
        	int id = self.getId();
        	
        	int minRange = getMinRange(id);
        	int maxRange = getMaxRange(id);
        	
        	int key = event.getKey();
        	
        	
        	if(key <= maxRange  && key >= minRange )
        	{
        		Integer value = keyStore.get(key);
        		System.out.println("["+id+"]: retrieved value successfully. Key:" 
        				+ key + " Value: "+ value);
        	}
        	else if(key > MAX_SYSTEM_RANGE || key < MIN_SYSTEM_RANGE)
        	{  		
        		System.out.println("Key out of range. Can't read.");
        	}        		
        	else
        	{
        		ReadValueEvent readValueEvent = new ReadValueEvent(self, successor, key);
        		System.out.println("["+id+"]: forwarding read to successor. Key:" + key);
        		trigger(readValueEvent, network);
        	}
        }
    };
    
    Handler<StoreKeyEvent> handleStoreKey = new Handler<StoreKeyEvent>() {
        public void handle(StoreKeyEvent event) {
        	int id = self.getId();
        	
        	int lowerRange = getMinRange(id);
        	int higherRange = getMaxRange(id);
        	
        	int key = event.getKey();        	
        	
        	if(key <= higherRange  && key >= lowerRange )
        	{
        		keyStore.put(key, event.getValue());
        		System.out.println("["+id+"]: stored key successfully. Key:" + key);
        	}
        	else if(key > MAX_SYSTEM_RANGE || key < MIN_SYSTEM_RANGE)
        	{  		
        		System.out.println("Key out of range. Can't store");
        	}        		
        	else
        	{
        		StoreKeyEvent storeKeyEvent = new StoreKeyEvent(self, successor, key, event.getValue());
        		System.out.println("["+id+"]: forwarding to successor. Key:" + key);
        		trigger(storeKeyEvent, network);
        	}
        }
    };
    
	Handler<PeerContInit> handleInit = new Handler<PeerContInit>() {
		public void handle(PeerContInit event) {			
			successor = event.getSuccessor();
			self = event.getSelf();
		}
	};
	
	// Util methods
	
	int getMaxRange(int id)
	{
		return id*100;
	}
	
	int getMinRange(int id)
	{
		return ((id -1) * 100) + 1;
		
	}
	
}
